当前位置: PHP > redis中setbit(位操作)的实际应用

redis中setbit(位操作)的实际应用

2022-06-18 分类:PHP 作者:admin 阅读(26)

位操作,只有两个值,0和1,8个位正好是1b,所以位操作是非常节省空间的一种操作。

在redis中他的用法也非常简单, 基本语法如下:

redis 127.0.0.1:6379> Setbit KEY_NAME OFFSET


原理部分:这个是SETBIT使用方法的简单说明

在redis中,存储的字符串都是以二级制的进行存在的。

举例:

设置一个 key-value ,键的名字叫“andy” 值为字符'a'

我们知道 'a' 的ASCII码是 97。转换为二进制是:01100001。offset的学名叫做“偏移” 。二进制中的每一位就是offset值啦,比如在这里 offset 0 等于 ‘0’ ,offset 1等于'1' ,offset2等于'1',offset 7 等于'1' ,没错,offset是从左往右计数的,也就是从高位往低位。

我们通过SETBIT 命令将 andy中的 'a' 变成 'b' 应该怎么变呢?

也就是将 01100001 变成 01100010 (b的ASCII码是98),这个很简单啦,也就是将'a'中的offset 6从0变成1,将offset 7 从1变成0 。

大家可能也发现了,每次SETBIT完毕之后,有一个(integer) 0或者(integer)1的返回值,这个是在你进行SETBIT 之前,该offset位的比特值

这个时候,我们再get andy 一下,看看结果:

果然,就从'a' 变成 'b'了。

这就是redis 中 “SETBIT” 的基本用法。

BITCOUNT 就是统计字符串的二级制码中,有多少个'1'。 所以在这里,

BITCOUNT andy 得到的结果就是 3 啦。

一个字符最大255,二进制是11111111, 只有8位,所有最多压缩8倍存储空间


其实就是把某个位标记为1或者0而已,但是它的好处在于非常节省空间。另外既然是位,就会涉及到或运算或者与运算(后面会有实例)。

我们来看一个实例吧

场景: 1亿个用户,每个用户登陆/做任意操作,记为 今天活跃,否则记为不活跃。

每周评出: 有奖活跃用户: 连续7天活动

每月评,等等...

其实简单说就是统计一下连续7天(或者连续30天)有多少人连续登陆过

咱们先来想一想传统的方案

很容易就会想到只要用户登陆了,我在表中插入一条数据,并且记录上对应的日期,然后用mysql里面的记录来逐个判断。

但这样是存在一些问题的,主要的问题在于用户量高达1亿,每个用户登陆一次就远远的超过mysql的极限了,更不要说统计一星期了,而且用上group ,sum运算,计算也是非常慢的。所以在这种用户量大,而且统计比较简单的问题上,咱们可以运用位(setbit)操作来解决问题。

先分析一下思路,对于某一天来说,我们可以把这一天想像成一根小木棍,分成了不同的段落,每个段落对应的就是用户的位(因为有user_id),默认值都是0,只要有人登陆了,就把对应的用户的位置标为1即可。

如上图所示,这个就是一天的登陆情况,user_id为6和user_id为8的用户登陆过。其余的都为没有登陆过。因为这个是位操作,所以占的空间很小,1亿的用户,所占的空间也就不到12M。

一天的问题咱们解决了,如何解决他们是否连续登陆过呢?

我们可以用上多个"木棍"

我们可以把每一天作为一个键,然后每天对用户登陆状态进行标记,在最后用每天做一个"与运算"就可以准确的知道哪些用户连续登陆了。

其实总结一下过程如下:

1、记录用户登陆:

每天按日期生成一个位图, 用户登陆后,把user_id位上的bit值置为1

2:、把1周的位图 and 计算,

位上为1的,即是连续登陆的用户

如上例,优点为:

1、节约空间, 1亿人每天的登陆情况,用1亿bit,约1200WByte,约10M 的字符就能表示;

2、计算方便。


拓展:

上一个案例,因为要计算连续登陆,所以用每年做key 而不是uid

用UID做key,方便展示每个用户登陆信息。

决定使用哪种方式前,了解位计算函数。

一、基本用法

任何一门程序语言都离不开位运算这个功能,redis虽然不是一门编程语言,但也是一个和编程密切关联的工具。因此位运算自然也是redis中不可或缺的功能。

redis中位运算相关的方法:

  • GETBIT key offset:获取第offset位的bit,不存的的比特位返回0。
  • SETBIT key offset value:给第offset位设置成value。
  • BITCOUNT key [start] [end]:计算key中1的个数。
  • BITOP operation destkey key [key]:执行位操作,位操作包含与(AND)、或(OR)、异或(XOR)以及 非(NOT)。
  • BITPOS key value [start] [end]:查询key中第一次出现value的位置,start和end表示字符的开始和结束位置。

二、使用示例

以字符串maqian为示例,对应的ASCII码和二进制位如下所示:

Redis中的位运算

对redis而言,它不会和我们一样给每个字符一一对应到二进制数据,对计算机而言,它能看到的就是图中第三行的数据。

2.1 获取比特

给name设置值为maqian,获取前8个字节分别对应m的比特位:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
127.0.0.1:6379> set name maqian
OK
127.0.0.1:6379> getbit name 0
(integer) 0
127.0.0.1:6379> getbit name 1
(integer) 1
127.0.0.1:6379> getbit name 2
(integer) 1
127.0.0.1:6379> getbit name 3
(integer) 0
127.0.0.1:6379> getbit name 4
(integer) 1
127.0.0.1:6379> getbit name 5
(integer) 1
127.0.0.1:6379> getbit name 6
(integer) 0
127.0.0.1:6379> getbit name 7
(integer) 1

2.2 计算1的个数

maqian 字符串中1的个数为5+3+4+4+3+5=24个,可以使用redis获取这个值:

1
2
3
4
127.0.0.1:6379> bitcount name
(integer) 24
127.0.0.1:6379> bitcount name 0 1 # 只计算前面两个字符ma中1的个数:5 + 3 = 8
(integer) 8

2.3 修改比特位

把第m 的第六位变成1,即相当于把m的ascii码+2,此时字符表示的是o :

1
2
3
4
127.0.0.1:6379> setbit name 6 1
(integer) 0
127.0.0.1:6379> get name
"oaqian"

2.4 逻辑操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6379> set a a
OK
127.0.0.1:6379> set b b
OK
127.0.0.1:6379> bitop and dst a b  # 与
(integer) 1
127.0.0.1:6379> get dst
"`"
127.0.0.1:6379> bitop or dst a b  # 或
(integer) 1
127.0.0.1:6379> get dst
"c"
127.0.0.1:6379> bitop xor dst a b  # 异或
(integer) 1
127.0.0.1:6379> get dst
"x03"

想要准确长久记住这些逻辑运算,有一个对应法则口诀:

与:and -> 有0出0,全1出1 例如:1 ,1–>1 1 ,0–>0 0 ,1–>0 0 ,0–>0

或:or -> 有1出1,全0出0 例如:1 ,1–>1 1 ,0–>1 0 ,1–>1 0 ,0–>0

非:not ->有1出0,有0出1 例如:1 -->0 0–>1

与非:nand ->先按与的操作,然后结果取反 例如:1 ,1–>0 1 ,0–>1 0 ,1–>1 0 ,0–>1

或非:nor ->先按或的操作,然后结果取反 例如:1 ,1–>0 1 ,0–>0 0 ,1–>0 0 ,0–>1

异或:xor ->相异为1,相同为0 例如:1 ,1-->0 1 ,0-->1 0 ,1-->1 0 ,0-->0 同或:xnor ->相同为1,相异为0 例如:1 ,1-->1 1 ,0-->0 0 ,1-->0 0 ,0-->1

2.5 查询第一个1的位置

1
2
3
4
5
6
127.0.0.1:6379> set name maqian
OK
127.0.0.1:6379> bitpos name 1 # 查询整个字符串第一个1的位置
(integer) 1
127.0.0.1:6379> bitpos name 1 2 3  # 查询第三个字符和第四个字符qi中第一次出现1的位置
(integer) 17

来源:

https://www.zhihu.com/question/27672245/answer/123641959

https://www.dyxmq.cn/databases/redis/redis-bit-operator.html

「三年博客,如果觉得我的文章对您有用,请帮助本站成长」

赞(0) 打赏

支付宝
微信
0

支付宝
微信
标签:

上一篇:

下一篇:

你可能感兴趣

共有 0 - redis中setbit(位操作)的实际应用

博客简介

精彩评论

  • admin(6年前 (2020-03-09))

    分别用不同厚度的筏板定义,画图后这设置筏板变截面处理。 http://f.fwxgx.co...

    评:新文章!
  • admin(6年前 (2020-03-09))

    分别用不同厚度的筏板定义,画图后这设置筏板变截面处理。 http://f.fwxgx.co...

    评:新文章!
  • admin(6年前 (2020-03-09))

    新增一个框架图! http://biji.jinli.vip/wp-content/upl...

    评:新文章!
  • 一位WordPress评论者(6年前 (2020-02-13))

    嗨,这是一条评论。 要开始审核、编辑及删除评论,请访问仪表盘的“评论”页面。 评论者头像来自...

    评:世界,您好!